home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
EnigmA Amiga Run 1999 March
/
EnigmA AMIGA RUN 35 (1999)(G.R. Edizioni)(IT)[!][issue 1999-03].iso
/
earcd
/
devel
/
vbcc-68k-src
/
vlink
/
t_amigaos.c
< prev
next >
Wrap
C/C++ Source or Header
|
1999-01-01
|
47KB
|
1,515 lines
/* $VER: vlink t_amigaos.c V0.6c (05.02.99)
*
* This file is part of vlink, a portable linker for multiple
* object formats.
* Copyright (c) 1997-99 Frank Wille
*
* vlink is freeware and part of the portable and retargetable ANSI C
* compiler vbcc, copyright (c) 1995-99 by Volker Barthelmann.
* vlink may be freely redistributed as long as no modifications are
* made and nothing is charged for it. Non-commercial usage is allowed
* without any restrictions.
* EVERY PRODUCT OR PROGRAM DERIVED DIRECTLY FROM MY SOURCE MAY NOT BE
* SOLD COMMERCIALLY WITHOUT PERMISSION FROM THE AUTHOR.
*
*
* v0.6c (05.02.98) phx
* __ctors/__dtors for executables only.
* v0.6a (19.12.98) phx
* Support for little endian object file formats.
* v0.6 (24.10.98) phx
* Take base register section offset from FFFuncs.baseoff.
* v0.5f (08.10.98) phx
* The automatic constructor/destructor functions have to begin
* with __INIT or __EXIT to avoid conflicts with ANSI-C identifiers.
* v0.5e (05.10.98) phx
* Global symbols beginning with _INIT or _EXIT will create an
* entry into the constructor/destructor function pointer table.
* These tables can be addressed by using the symbols __ctors
* and __dtors (e.g. in the startup/cleanup code of a program).
* The priority of these functions may be defined by specifying
* a number behind the INIT/EXIT string. Example: _INIT_9_OpenLibs.
* Otherwise their priority defaults to 0 and they will be
* positioned in order of occurence.
* v0.5d (22.08.98) phx
* Faster memory allocation can be activated by #define FASTALLOC.
* v0.5c (08.07.98) phx
* ehf_lnksym():
* Target amigaehf supports the automatic generation of "@__name"
* pointer variables in ".tocd". @__name will not be created, if
* _name is undefined too. Then it's assumed to be a real unknown
* symbol reference.
* v0.5 (27.06.98) phx
* Target-specific linker symbol support: ados_lnksym(), ehf_lnksym(),
* ados_setlnksym(), ehf_setlnksym().
* Linker symbols currently supported by ADOS and EHF:
* _DATA_BAS_,_DATA_LEN_,_BSS_LEN_,_LinkerDB,__BSSBAS,__BSSLEN,
* __ctors,__dtors,__DATA_BAS,__DATA_LEN,__BSS_LEN,__RESIDENT.
* v0.4 (05.06.98) phx
* Support for Code-Bss and Data-Bss (small data) sections.
* New FFF targetlink(). Checks for __MERGED, _NOMERGE and
* unnamed sections.
* v0.3b (02.05.98) phx
* Creates a dummy code section, if an output file has no
* section at all.
* v0.3 (12.04.98) phx
* fwrite32() is now called as fwrite32be().
* Uses new addreloc() from targets.c.
* addxref() requires addend.
* Ignore absolute local symbols for object files and ignore
* absolute local and global symbols for executable files.
* v0.2 (07.03.98) phx
* Unnamed sections of an executable will always get different
* names, to avoid that they are linked together.
* HUNK_DREL32 must be treated as HUNK_RELOC32SHORT, if the
* input file is executable.
* Number of hunks in HUNK_HEADER was wrong, if resident library
* name is missing (which is always the case).
* v0.1 (27.02.98) phx
* First version that seems to link AmigaOS ADOS and EHF
* objects and libraries. Many common features, like linking
* sections together which have relative references, are
* still missing. Also, PowerPC-ELF32 support is about to come.
* v0.0 (02.12.97) phx
* File created.
*/
#if defined(ADOS) || defined(EHF)
#define T_AMIGAOS_C
#include "vlink.h"
#include "amigahunks.h"
#define BSSDEFAULTNAME "BSS"
#define EXT_IGNORE 0x100
struct HunkInfo {
uint8 *hunkbase; /* base address of amigaos/ehf file */
uint8 *hunkptr; /* current hunk data pointer */
long hunkcnt; /* remaining bytes in this file */
char *filename;
bool exec; /* executable file? */
};
struct XRefNode {
struct node n;
char *sym_name;
uint8 ref_type;
int noffsets;
struct list xreflist;
};
static int ados_identify(char*,uint8 *,unsigned long);
static int ehf_identify(char *,uint8 *,unsigned long);
static int identify(char *,uint8 *,unsigned long);
static void readconv(struct GlobalVars *,struct LinkFile *);
static unsigned long secbase(struct GlobalVars *,char *);
static uint8 cmpsecflags(uint8,uint8);
static struct Section *bssdefault(struct ObjectUnit *);
static int targetlink(struct GlobalVars *,struct LinkedSection *,
struct Section *);
static struct Symbol *ados_lnksym(struct GlobalVars *,struct Section *,
struct XReference *);
static void ados_setlnksym(struct GlobalVars *,struct Symbol *,
struct XReference *);
static struct Symbol *ehf_lnksym(struct GlobalVars *,struct Section *,
struct XReference *);
static void ehf_setlnksym(struct GlobalVars *,struct Symbol *,
struct XReference *);
static int get_xtors_pri(char *);
static void create_xdef(struct GlobalVars *,struct Section *,char *,
uint32,uint8,uint8);
static void create_xrefs(struct GlobalVars *,struct HunkInfo *,
struct Section *,char *,uint8,uint8);
static char *gethunkname(struct HunkInfo *);
static bool addlongrelocs(struct GlobalVars *,struct HunkInfo *,
struct Section *,uint8);
static bool addshortrelocs(struct GlobalVars *,struct HunkInfo *,
struct Section *,uint8);
static void init_hunkinfo(struct HunkInfo *,char *,uint8 *,unsigned long);
static uint32 skiphunk(struct HunkInfo *);
static void movehunkptr32(struct HunkInfo *,uint32);
static void movehunkptr16(struct HunkInfo *,uint32);
static void alignhunkptr(struct HunkInfo *);
static uint32 testword32(struct HunkInfo *);
static uint32 nextword32(struct HunkInfo *);
static uint16 nextword16(struct HunkInfo *);
static void writeobject(struct GlobalVars *,FILE *);
static void writeshared(struct GlobalVars *,FILE *);
static void writeexec(struct GlobalVars *,FILE *);
static void hunk_name_len(FILE *,char *);
static void hunk_name(FILE *,char *);
static int strlen32(char *);
static void reloc_hunk(FILE *,struct LinkedSection *,struct GlobalVars *,
uint8,uint32);
static void unsupp_relocs(struct LinkedSection *);
static void ext_refs(FILE *,struct LinkedSection *);
static void ext_defs(FILE *,struct LinkedSection *,uint8,uint8,uint32);
static void unsupp_symbols(struct LinkedSection *);
struct FFFuncs fff_amigaos = {
"amigaos",
ados_identify,
readconv,
secbase,
cmpsecflags,
bssdefault,
targetlink,
ados_lnksym,
ados_setlnksym,
writeobject,
writeshared,
writeexec,
0x7ffe,
NULL,
1 /* big endian */
};
struct FFFuncs fff_ehf = {
"amigaehf",
ehf_identify,
readconv,
secbase,
cmpsecflags,
bssdefault,
targetlink,
ehf_lnksym,
ehf_setlnksym,
writeobject,
writeshared,
writeexec,
0x7ffe,
NULL,
1 /* big endian */
};
/* Automagically create symbols in .tocd, which start with the */
/* following letters: */
static char ehf_addrsym[] = "@_";
static char *ados_symnames[] = {
/* PhxAss */ "_DATA_BAS_","_DATA_LEN_","_BSS_LEN_",
/* SAS/StormC */ "_LinkerDB","__BSSBAS","__BSSLEN","__ctors","__dtors",
/* DICE-C */ "__DATA_BAS","__DATA_LEN","__BSS_LEN","__RESIDENT"
};
#define PHXDB 0
#define PHXDL 1
#define PHXBL 2
#define SASPT 3
#define SASBB 4
#define SASBL 5
#define SASCT 6
#define SASDT 7
#define DICDB 8
#define DICDL 9
#define DICBL 10
#define DICRS 11
#define LAST_LNKSYM DICRS
/* automatic constructors and destructors */
static char vbcc_INIT[] = "__INIT";
static char vbcc_EXIT[] = "__EXIT";
static char xtors_objname[] = "INITEXIT"; /* unit name for con/destructors */
static bool exthunk,symhunk;
/*****************************************************************/
/* Read ADOS / EHF */
/*****************************************************************/
static int ados_identify(char *name,uint8 *p,unsigned long plen)
{
int ff;
if ((ff = identify(name,p,plen)) >= 0)
return (ff);
return (ID_UNKNOWN); /* ehf */
}
static int ehf_identify(char *name,uint8 *p,unsigned long plen)
{
int ff;
if ((ff = identify(name,p,plen)) < 0)
return (-ff);
return (ff); /* ados is a subset of ehf */
}
static int identify(char *name,uint8 *p,unsigned long plen)
/* Identify AmigaDOS or extended hunk format */
{
uint32 w = read32be(p);
struct HunkInfo hi;
int type;
if (w == HUNK_HEADER) {
error(12,name); /* file is already an executable */
return (ID_EXECUTABLE); /* EHF-executables doesn't exist! */
}
else if (w!=HUNK_UNIT && w!=HUNK_LIB)
return (ID_UNKNOWN);
/* @@@ EHF will never be in HUNK_LIB format, because HUNK_PPC_CODE */
/* (0x4e9) conflicts with HUNK_CODE|(MEMB_CHIP<<14) = 0x7e9. @@@ */
if (w == HUNK_LIB)
return (ID_LIBARCH); /* SAS/C-style library */
/* Check, by comparing the suffix with ".LIB", if the units */
/* of this object file have to be handled as part of an object */
/* or library archive. Yes, this is the only way... */
type = ID_OBJECT;
if ((w = strlen(name)-4) > 0) {
if (name[w]=='.' &&
toupper((unsigned char)name[w+1])=='L' &&
toupper((unsigned char)name[w+2])=='I' &&
toupper((unsigned char)name[w+3])=='B')
type = ID_LIBARCH;
}
/* Now check if any unit contains EHF hunks */
init_hunkinfo(&hi,name,p,plen);
while (w = skiphunk(&hi)) {
if (w == HUNK_PPC_CODE)
return (-type); /* it's an EHF object/library, containing PPC code */
}
return (type);
}
static void readconv(struct GlobalVars *gv,struct LinkFile *lf)
/* Read AmigaDOS/EHF hunks and symbols */
{
struct HunkInfo hi;
uint32 w;
struct ObjectUnit *u=NULL;
struct Section *s=NULL;
char *secname=NULL;
uint8 *headerattr=NULL;
init_hunkinfo(&hi,lf->filename,lf->data,lf->length);
if (testword32(&hi) == HUNK_LIB) {
ierror("readconv(): HUNK_LIB not yet supported");
}
/* HUNK_UNIT or HUNK_HEADER */
else {
while (w = testword32(&hi)) {
switch (w & 0x3fffffff) {
case HUNK_HEADER:
/* header must be at the beginning of the file */
if (hi.hunkptr == hi.hunkbase) {
if (!s) {
uint32 n;
add_objunit(gv,u,TRUE); /* add last one to glob. ObjUnit list */
u = create_objunit(lf,lf->filename);
nextword32(&hi);
n = nextword32(&hi);
movehunkptr32(&hi,n?n+2:1);
w = nextword32(&hi);
n = nextword32(&hi) - w + 1; /* number of size specifiers */
headerattr = hi.hunkptr; /* remember section attributes */
while (n--) {
if ((nextword32(&hi) & 0xc0000000) == 0xc0000000)
nextword32(&hi);
}
}
else
/* Unexpected end of section */
error(15,lf->pathname,s->name,u->objname);
}
else
/* header appeared twice */
error(16,lf->pathname,"HUNK_HEADER",lf->filename);
break;
case HUNK_UNIT: /* a new ObjectUnit */
if (!s) {
char *uname;
nextword32(&hi);
add_objunit(gv,u,TRUE); /* add last one to glob. ObjUnit list */
uname = gethunkname(&hi);
u = create_objunit(lf,uname);
}
else
/* Unexpected end of section */
error(15,lf->pathname,s->name,u->objname);
break;
case HUNK_NAME: /* name of a section */
if (u && !s) {
nextword32(&hi);
secname = gethunkname(&hi);
}
else
/* misplaced hunk */
error(17,lf->pathname,"HUNK_NAME",u?u->objname:lf->filename);
break;
case HUNK_CODE:
case HUNK_PPC_CODE:
case HUNK_DATA:
case HUNK_BSS:
if (u && !s) { /* a new Section */
uint8 s_attr;
unsigned long s_size;
nextword32(&hi);
/* determine section attributes */
if (headerattr) {
if ((s_attr = (read32be(headerattr)>>24) & 0xc0) == 0xc0) {
s_attr = 0; /* ext. mem. type identifier is not supported */
headerattr += 8;
}
else
headerattr += 4;
}
else
s_attr = (w>>24) & 0xc0; /* SF_FAST | SF_CHIP */
/* Sections of an executable should never be joined, or */
/* dangerous things will happen. */
if (!secname && hi.exec) {
secname = alloc(10);
sprintf(secname,"S%08lx",(unsigned long)hi.hunkptr);
}
/* create new section node */
s_size = (unsigned long)nextword32(&hi) & 0x3fffffff;
s = create_section(u,secname,hi.hunkptr,s_size<<2);
s->flags = s_attr;
s->alignment = 2; /* amigaos/ehf always 32-bit aligned */
switch (w & 0xffff) {
case HUNK_PPC_CODE:
s->flags |= SF_EHFPPC; /* section contains PowerPC code */
case HUNK_CODE:
s->type = ST_CODE;
/* @@@@ amigaos/ehf code is always writeable ??? */
s->protection = SP_READ | /*SP_WRITE |*/ SP_EXEC;
break;
case HUNK_DATA:
s->type = ST_DATA;
s->protection = SP_READ | SP_WRITE;
break;
case HUNK_BSS:
s->flags |= SF_UNINITIALIZED;
s->data = NULL;
s->type = ST_UDATA;
s->protection = SP_READ | SP_WRITE;
break;
}
if (!(s->flags & SF_UNINITIALIZED))
movehunkptr32(&hi,s_size); /* skip section's contents */
}
else
/* Section appeared twice */
error(16,lf->pathname,"Section",u?u->objname:lf->filename);
break;
case HUNK_ABSRELOC32:
if (!addlongrelocs(gv,&hi,s,R_ADDR32))
error(17,lf->pathname,"HUNK_ABSRELOC32",u?u->objname:lf->filename);
break;
case HUNK_RELOC32SHORT:
if (!addshortrelocs(gv,&hi,s,R_ADDR32))
error(17,lf->pathname,"HUNK_RELOC32SHORT",u?u->objname:lf->filename);
break;
case HUNK_RELRELOC16:
if (!addlongrelocs(gv,&hi,s,R_REL16))
error(17,lf->pathname,"HUNK_RELRELOC16",u?u->objname:lf->filename);
break;
case HUNK_RELRELOC8:
if (!addlongrelocs(gv,&hi,s,R_REL8))
error(17,lf->pathname,"HUNK_RELRELOC8",u?u->objname:lf->filename);
break;
case HUNK_RELRELOC32:
if (!addlongrelocs(gv,&hi,s,R_REL32))
error(17,lf->pathname,"HUNK_RELRELOC32",u?u->objname:lf->filename);
break;
case HUNK_ABSRELOC16:
if (!addlongrelocs(gv,&hi,s,R_ADDR16))
error(17,lf->pathname,"HUNK_ABSRELOC16",u?u->objname:lf->filename);
break;
case HUNK_DREL32:
/* This hunk block has a double meaning. Before Amiga OS 3.0 */
/* it was often used as RELOC32SHORT, but will appear in */
/* executables only. */
if (hi.exec) {
if (!addshortrelocs(gv,&hi,s,R_ADDR32))
error(17,lf->pathname,"HUNK_RELOC32SHORT",
u?u->objname:lf->filename);
}
else {
if (!addlongrelocs(gv,&hi,s,R_BASEREL32))
error(17,lf->pathname,"HUNK_DREL32",u?u->objname:lf->filename);
}
break;
case HUNK_DREL16:
if (!addlongrelocs(gv,&hi,s,R_BASEREL16))
error(17,lf->pathname,"HUNK_DREL16",u?u->objname:lf->filename);
break;
case HUNK_DREL8:
if (!addlongrelocs(gv,&hi,s,R_BASEREL8))
error(17,lf->pathname,"HUNK_DREL8",u?u->objname:lf->filename);
break;
case HUNK_RELRELOC26: /* EHF */
if (!addlongrelocs(gv,&hi,s,R_REL26))
error(17,lf->pathname,"HUNK_RELRELOC26",u?u->objname:lf->filename);
break;
case HUNK_EXT: /* external definitions and references */
case HUNK_SYMBOL:
if (s) {
uint32 xtype,n;
char *xname;
nextword32(&hi);
while (xtype = testword32(&hi)) {
xname = gethunkname(&hi);
switch (xtype >>= 24) {
/* Symbol Definitions */
case EXT_SYMB: /* local symbol definition (for debugging) */
create_xdef(gv,s,xname,nextword32(&hi),
SYM_RELOC,SYMB_LOCAL);
break;
case EXT_DEF: /* global addr. symbol def. (requires reloc) */
create_xdef(gv,s,xname,nextword32(&hi),
SYM_RELOC,SYMB_GLOBAL);
break;
case EXT_ABS: /* global def. of absolute symbol */
create_xdef(gv,s,xname,nextword32(&hi),
SYM_ABS,SYMB_GLOBAL);
break;
case EXT_RES: /* unsupported type, invalid since OS2.0 */
error(18,lf->pathname,xname,u->objname,EXT_RES);
break;
/* Unresolved Symbol References */
case EXT_ABSREF32:
create_xrefs(gv,&hi,s,xname,R_ADDR32,4);
break;
case EXT_ABSREF16:
create_xrefs(gv,&hi,s,xname,R_ADDR16,2);
break;
case EXT_ABSREF8:
create_xrefs(gv,&hi,s,xname,R_ADDR8,1);
break;
case EXT_RELREF32:
create_xrefs(gv,&hi,s,xname,R_REL32,4);
break;
case EXT_RELREF26: /* EHF */
create_xrefs(gv,&hi,s,xname,R_REL26,4);
break;
case EXT_RELREF16:
create_xrefs(gv,&hi,s,xname,R_REL16,2);
break;
case EXT_RELREF8:
create_xrefs(gv,&hi,s,xname,R_REL8,1);
break;
case EXT_DEXT32:
create_xrefs(gv,&hi,s,xname,R_BASEREL32,4);
break;
case EXT_DEXT16:
create_xrefs(gv,&hi,s,xname,R_BASEREL16,2);
break;
case EXT_DEXT8:
create_xrefs(gv,&hi,s,xname,R_BASEREL8,1);
break;
case EXT_ABSCOMMON:
/* ABSCOMMON was never used and only supported */
/* by the VERY old ALink linker on AmigaOS. */
/* But I like it... :) */
create_xdef(gv,s,xname,nextword32(&hi),
SYM_COMMON,SYMB_GLOBAL);
create_xrefs(gv,&hi,s,xname,R_ADDR32,4);
break;
case EXT_RELCOMMON:
/* RELCOMMON was introduced in OS3.1 and is */
/* neither used nor supported by any known software. */
/* It makes no sense for me too... */
error(20,lf->pathname,xname,u->objname,xtype>>24);
break;
default: /* unsupported HUNK_EXT sub type */
if (xtype & 0x80000000)
error(20,lf->pathname,xname,u->objname,xtype>>24);
else
error(18,lf->pathname,xname,u->objname,xtype>>24);
}
}
nextword32(&hi);
}
else
error(17,lf->pathname,(w==HUNK_EXT) ? "HUNK_EXT" : "HUNK_SYMBOL",
u ? u->objname : lf->filename);
break;
case HUNK_DEBUG:
/* @@@@ currently ignored - will be implemented later */
skiphunk(&hi);
break;
case HUNK_OVERLAY:
case HUNK_BREAK:
ierror("readconv(): HUNK_OVERLAY/BREAK not yet supported");
break;
case HUNK_END:
nextword32(&hi);
if (s) {
addtail(&u->sections,&s->n); /* add Section to ObjectUnit */
s = NULL;
secname = NULL;
}
else
error(17,lf->pathname,"HUNK_END",u ? u->objname : lf->filename);
break;
default:
error(13,lf->pathname); /* File format corrupted */
break;
}
}
add_objunit(gv,u,TRUE); /* add last one to glob. ObjUnit list */
u = NULL;
}
}
static unsigned long secbase(struct GlobalVars *gv,char *name)
/* return default base address for a section with this name */
{
return (0); /* base address has no meaning for ados/ehf */
}
static uint8 cmpsecflags(uint8 oldflags,uint8 newflags)
/* compare and verify target-specific section flags, */
/* return 0xff if sections are incompatible, otherwise return new flags */
{
if (oldflags&SF_EHFPPC != newflags&SF_EHFPPC)
return (0xff);
if ((oldflags&(SF_CHIP|SF_FAST))==0 ||
(newflags&(SF_CHIP|SF_FAST))==0 ||
(oldflags&(SF_CHIP|SF_FAST)) == (newflags&(SF_CHIP|SF_FAST)))
return (newflags | (oldflags&(SF_CHIP|SF_FAST)));
return (0xff);
}
static struct Section *bssdefault(struct ObjectUnit *ou)
/* Create a default BSS section for ADOS/EHF */
{
static char *bssname = BSSDEFAULTNAME;
struct Section *s = create_section(ou,bssname,NULL,0);
s->flags |= SF_UNINITIALIZED;
s->type = ST_UDATA;
s->protection = SP_READ | SP_WRITE;
return (s);
}
static int targetlink(struct GlobalVars *gv,struct LinkedSection *ls,
struct Section *s)
/* returns 1, if target requires the combination of the two sections, */
/* returns -1, if target don't want to combine them, */
/* returns 0, if target doesn't care - standard linking rules are used. */
{
static char *merged = "__MERGED";
/* AmigaDOS doesn't merge unnamed sections, unless the Small- */
/* Data/Code option was set */
if (!(gv->small_code && s->type==ST_CODE) &&
!(gv->small_data && (s->type==ST_DATA || s->type==ST_UDATA)))
if (*(ls->name)==0 && *(s->name)==0)
return (-1);
/* sections with name "_NOMERGE" are never combined */
if (!strcmp(s->name,"_NOMERGE"))
return (-1);
/* Executable only: */
if (!gv->dest_object) {
if (!strcmp(ls->name,merged) && !strcmp(s->name,merged)) {
/* data and bss section with name __MERGED are always combined */
if (s->type == ST_CODE)
error(57,getobjname(s->obj)); /* Merging code section "__MERGED" */
return (1);
}
}
return (0);
}
static struct Symbol *ados_lnksym(struct GlobalVars *gv,struct Section *sec,
struct XReference *xref)
/* Checks, if undefined symbol is an ADOS-linker symbol */
{
struct Symbol *sym;
int i;
if (sym = findlnksymbol(&fff_amigaos,xref->name))
return (sym);
if (!gv->dest_object) {
for (i=0; i<=LAST_LNKSYM; i++) {
if (!strcmp(ados_symnames[i],xref->name)) {
sym = addlnksymbol(&fff_amigaos,NULL,ados_symnames[i],0,SYM_ABS,
SYMF_LNKSYM,SYMI_OBJECT,SYMB_GLOBAL,0);
sym->extra = i; /* for easy identification in ados_setlnksym */
if (i==PHXDB || i==SASPT || i==SASBB || i==DICDB)
sym->type = SYM_RELOC;
return (sym); /* new linker symbol created */
}
}
}
return (NULL);
}
static void ados_setlnksym(struct GlobalVars *gv,struct Symbol *xdef,
struct XReference *xref)
/* Initialize ADOS linker symbol structure during resolve_xref() */
{
if (xdef->flags & SYMF_LNKSYM) {
struct LinkedSection *sdsec = smalldata_section(gv);
struct Symbol *s;
xdef->relsect = (struct Section *)sdsec->sections.first;
switch (xdef->extra) {
case PHXDL:
case SASBB:
case DICDL:
xdef->value = sdsec->filesize;
break;
case PHXBL:
case SASBL:
case DICBL:
xdef->value = sdsec->size - sdsec->filesize;
break;
case SASPT:
xdef->value = fff_amigaos.baseoff;
break;
case SASCT:
case SASDT:
if (s = findsymbol(gv,ados_symnames[xdef->extra])) {
/* a __ctors or __dtors is really defined: */
xdef->value = s->value;
xdef->relsect = s->relsect;
xdef->type = s->type;
xdef->flags = s->flags;
xdef->info = s->info;
xdef->bind = s->bind;
xdef->size = s->size;
}
break;
}
xdef->flags &= ~SYMF_LNKSYM; /* do not init again */
}
}
static struct Symbol *ehf_lnksym(struct GlobalVars *gv,struct Section *sec,
struct XReference *xref)
/* Checks, if undefined symbol is an EHF-linker symbol */
{
struct Symbol *sym;
#if 0
/*@@@ findlnksymbol is not used, because @__xxx are real symbols */
if (sym = findlnksymbol(&fff_ehf,xref->name))
return (sym);
#endif
if (!gv->dest_object) {
if (!strncmp(ehf_addrsym,xref->name,sizeof(ehf_addrsym)-1)) {
char *symname = xref->name + (sizeof(ehf_addrsym) - 1);
if (findsymbol(gv,symname)) {
/* Reference to an unknown symbol, which starts with "@_". */
/* The symbol without "@_" has to exist. */
char *objname = alloc(strlen(symname)+3);
uint8 *dat = alloczero(sizeof(uint32)); /* space for a 32-bit ptr */
struct ObjectUnit *ou;
struct Section *s;
struct Symbol *sym;
/* create an artificial object with a ".tocd" data section */
sprintf(objname,"%s.o",symname);
ou = art_objunit(objname,dat,sizeof(uint32),gv->dest_format);
s = create_section(ou,".tocd",dat,sizeof(uint32));
s->type = ST_DATA;
s->protection = SP_READ | SP_WRITE;
addtail(&ou->sections,&s->n);
/* "@__name" contains a 32-bit reloc pointer, which has an */
/* external reference to the symbol "_name". */
addxref(gv,s,symname,0,R_ADDR32,sizeof(uint32),0);
/* make "@__name" visible for the further linking process: */
if (addsymbol(gv,s,xref->name,0,SYM_RELOC,0,SYMI_OBJECT,
SYMB_GLOBAL,sizeof(uint32)))
ierror("ehf_lnksym(): %s was assumed to be undefined, but "
"in reality it *is* defined",xref->name);
if (!(sym = findsymbol(gv,xref->name)))
ierror("ehf_lnksym(): The just defined symbol %s has "
"disappeared",xref->name);
return (sym);
}
}
}
return (ados_lnksym(gv,sec,xref));
}
static void ehf_setlnksym(struct GlobalVars *gv,struct Symbol *xdef,
struct XReference *xref)
/* Initialize EHF linker symbol structure during resolve_xref() */
{
ados_setlnksym(gv,xdef,xref);
}
static int get_xtors_pri(char *s)
/* Return priority of a constructor/destructor function name. */
/* Its priority may be specified by a number behind the 2nd underscore. */
/* Example: __INIT_9_OpenLibs (constructor with priority 9) */
{
if (*s++ == '_')
if (isdigit((unsigned)*s))
return (atoi(s));
return (0);
}
static void create_xdef(struct GlobalVars *gv,struct Section *s,
char *name,uint32 val,uint8 type,uint8 bind)
/* add new symbol to the symbols list of the current object unit */
{
struct Symbol *sym;
uint32 size = 0;
#ifdef DELUNDERSCORE
if (*name == '_')
++name; /* internal representation is without leading '_' */
#endif
if (type == SYM_COMMON) {
/* Oh, a common symbol? A rare guest. :) */
size = val; /* its size was stored in val */
val = 2; /* how about a 32-bit alignment? */
}
if (sym = addsymbol(gv,s,name,val,type,0,SYMI_NOTYPE,bind,size)) {
/* Symbol already defined. If defined globally, ignore it. */
/* Otherwise, change into a global definition. */
if (sym->bind != SYMB_GLOBAL) {
if (bind == SYMB_GLOBAL) {
sym->value = val; /* redefine as global (xdef) symbol */
sym->relsect = s;
sym->type = type;
sym->flags = 0;
sym->info = SYMI_NOTYPE;
sym->bind = SYMB_GLOBAL;
sym->size = size;
addglobsym(gv,sym);
}
}
}
if (bind == SYMB_GLOBAL && !gv->dest_object) {
/* symbol describes an automatic constructor/destructor function ? */
if (!strncmp(vbcc_INIT,name,sizeof(vbcc_INIT)-1))
new_priptr(s->obj,xtors_objname,ados_symnames[SASCT],
get_xtors_pri(name+(sizeof(vbcc_INIT)-1)),name,0);
else if (!strncmp(vbcc_EXIT,name,sizeof(vbcc_EXIT)-1))
new_priptr(s->obj,xtors_objname,ados_symnames[SASDT],
get_xtors_pri(name+(sizeof(vbcc_EXIT)-1)),name,0);
}
}
static void create_xrefs(struct GlobalVars *gv,struct HunkInfo *hi,
struct Section *s,char *name,uint8 rtype,uint8 size)
/* add new unresolved symbol reference to the current section */
{
uint32 n = nextword32(hi); /* number of references */
uint32 offs;
#ifdef DELUNDERSCORE
if (*name == '_')
++name; /* internal representation is without leading '_' */
#endif
while (n--) {
offs = nextword32(hi);
addxref(gv,s,name,offs,rtype,size,readsection(gv,s->data+offs,rtype));
}
}
static char *gethunkname(struct HunkInfo *hi)
/* read 32-bit aligned name, the most significant byte of the */
/* length specifier is ignored (for HUNK_EXT names, for example) */
{
uint32 n;
char *s=NULL;
if (n = nextword32(hi) & 0xffffff) { /* name given? */
s = alloczero((n+1)<<2);
strncpy(s,(char *)hi->hunkptr,n<<2);
movehunkptr32(hi,n);
}
return (s);
}
static bool addlongrelocs(struct GlobalVars *gv,struct HunkInfo *hi,
struct Section *s,uint8 type)
/* convert an amigaos/ehf relocation hunk into an internal Reloc node */
{
struct Reloc *r;
uint32 n,id,offs;
if (s) {
nextword32(hi);
while (n = nextword32(hi)) {
id = nextword32(hi); /* add base addr. of section with this index */
while (n--) {
offs = nextword32(hi);
addreloc(s,NULL,id,offs,type,readsection(gv,s->data+offs,type));
}
}
return (TRUE);
}
return (FALSE);
}
static bool addshortrelocs(struct GlobalVars *gv,struct HunkInfo *hi,
struct Section *s,uint8 type)
/* convert an amigaos/ehf relocation hunk into an internal Reloc node */
{
struct Reloc *r;
uint32 n,id,offs;
if (s) {
nextword32(hi);
while (n = (uint32)nextword16(hi)) {
id = (uint32)nextword16(hi); /* add base addr. of sec. with this idx. */
while (n--) {
offs = (uint32)nextword16(hi);
addreloc(s,NULL,id,offs,type,readsection(gv,s->data+offs,type));
}
}
alignhunkptr(hi);
return (TRUE);
}
return (FALSE);
}
static void init_hunkinfo(struct HunkInfo *hi,char *name,uint8 *p,
unsigned long plen)
{
hi->hunkptr = hi->hunkbase = p;
hi->hunkcnt = (long)plen;
hi->filename = name;
hi->exec = read32be(p) == HUNK_HEADER; /* executable file? */
}
static uint32 skiphunk(struct HunkInfo *hi)
/* Skip the hunk where hunkptr points to. */
/* Return first word of next hunk. */
{
uint32 type,n;
switch (type = read32be(hi->hunkptr) & 0x3fffffff) {
case HUNK_UNIT:
case HUNK_NAME:
case HUNK_CODE:
case HUNK_DATA:
case HUNK_DEBUG:
case HUNK_LIB:
case HUNK_INDEX:
case HUNK_PPC_CODE: /* EHF */
movehunkptr32(hi,read32be(hi->hunkptr+4)+2);
break;
case HUNK_BSS:
movehunkptr32(hi,2); /* skip size specifier */
break;
case HUNK_ABSRELOC32:
case HUNK_RELRELOC16:
case HUNK_RELRELOC8:
case HUNK_RELRELOC32:
case HUNK_ABSRELOC16:
case HUNK_DREL32:
case HUNK_DREL16:
case HUNK_DREL8:
case HUNK_RELRELOC26: /* EHF */
if (!(type==HUNK_DREL32 && hi->exec)) {
nextword32(hi);
while (n = nextword32(hi))
movehunkptr32(hi,n+1);
break;
}
/* else, fall through to HUNK_RELOC32SHORT */
case HUNK_RELOC32SHORT:
nextword32(hi);
while (n = (uint32)nextword16(hi))
movehunkptr16(hi,n+1);
alignhunkptr(hi);
break;
case HUNK_EXT:
case HUNK_SYMBOL:
nextword32(hi);
while (n = nextword32(hi)) {
if (n & 0x80000000) { /* external reference */
movehunkptr32(hi,n & 0xffffff);
if ((n>>24)==EXT_ABSCOMMON || (n>>24)==EXT_RELCOMMON)
nextword32(hi); /* size of common area */
movehunkptr32(hi,nextword32(hi)); /* skip reference offsets */
}
else
movehunkptr32(hi,(n&0xffffff)+1); /* skip xdef-value */
}
break;
case HUNK_HEADER:
nextword32(hi);
movehunkptr32(hi,nextword32(hi)+2);
n = nextword32(hi); /* first root hunk index */
n = nextword32(hi) - n + 1; /* number of size specifiers */
while (n--) {
if ((nextword32(hi) & 0xc0000000) == 0xc0000000)
nextword32(hi);
}
break;
case HUNK_OVERLAY:
movehunkptr32(hi,read32be(hi->hunkptr+4)+3); /* @@@ I'm not sure...*/
break;
case HUNK_END:
case HUNK_BREAK:
nextword32(hi);
break;
default:
error(13,hi->filename); /* File format corrupted */
break;
}
/* return next 32-bit word */
return (testword32(hi));
}
static void movehunkptr32(struct HunkInfo *hi,uint32 n)
/* skip n 32-bit words */
{
long offset = (long)n << 2;
if ((hi->hunkcnt -= offset) < 0)
error(13,hi->filename); /* File format corrupted */
hi->hunkptr += offset;
}
static void movehunkptr16(struct HunkInfo *hi,uint32 n)
/* skip n 16-bit words */
{
long offset = (long)n << 1;
if ((hi->hunkcnt -= offset) < 0)
error(13,hi->filename); /* File format corrupted */
hi->hunkptr += offset;
}
static void alignhunkptr(struct HunkInfo *hi)
/* make hunk pointer 32-bit aligned */
{
if ((hi->hunkptr - hi->hunkbase) & 2)
movehunkptr16(hi,1);
}
static uint32 testword32(struct HunkInfo *hi)
/* try to read next 32-bit word - return 0, if end of file reached */
{
if (hi->hunkcnt >= sizeof(uint32))
return (read32be(hi->hunkptr));
else
return (0);
}
static uint32 nextword32(struct HunkInfo *hi)
/* read next 32-bit word */
{
uint32 w;
if ((hi->hunkcnt -= sizeof(uint32)) < 0)
error(13,hi->filename); /* File format corrupted */
w = read32be(hi->hunkptr);
hi->hunkptr += sizeof(uint32);
return (w);
}
static uint16 nextword16(struct HunkInfo *hi)
/* read next 16-bit word */
{
uint16 w;
if ((hi->hunkcnt -= sizeof(uint16)) < 0)
error(13,hi->filename); /* File format corrupted */
w = read16be(hi->hunkptr);
hi->hunkptr += sizeof(uint16);
return (w);
}
/*****************************************************************/
/* Write ADOS / EHF */
/*****************************************************************/
static void writeshared(struct GlobalVars *gv,FILE *f)
{
error(30); /* Target file format doesn't support shared objects */
}
static void writeobject(struct GlobalVars *gv,FILE *f)
/* creates a target-amigaos relocatable object file */
{
struct LinkedSection *ls = (struct LinkedSection *)gv->lnksec.first;
struct LinkedSection *nextls;
fwrite32be(f,HUNK_UNIT);
hunk_name_len(f,gv->dest_name); /* unit name is output file name */
if (ls->n.next == NULL) {
/* special case: no sections, create dummy section */
fwrite32be(f,HUNK_CODE);
fwrite32be(f,0);
fwrite32be(f,HUNK_END);
return;
}
/* section loop */
while (nextls = (struct LinkedSection *)ls->n.next) {
uint32 memflags = (ls->flags&(SF_FAST|SF_CHIP))<<24;
exthunk = symhunk = FALSE;
fwrite32be(f,HUNK_NAME);
hunk_name_len(f,ls->name); /* section name */
switch (ls->type) { /* section type */
case ST_CODE:
if (ls->flags & SF_EHFPPC)
fwrite32be(f,HUNK_PPC_CODE|memflags);
else
fwrite32be(f,HUNK_CODE|memflags);
break;
case ST_DATA:
fwrite32be(f,HUNK_DATA|memflags);
break;
case ST_UDATA:
fwrite32be(f,HUNK_BSS|memflags);
break;
default:
ierror("writeobject(): Illegal section type %d",ls->type);
break;
}
fwrite32be(f,(ls->size+3)>>2); /* section size */
if (!(ls->flags & SF_UNINITIALIZED)) {
fwritex(f,ls->data,ls->size); /* write section contents */
fwrite_align(f,2,ls->size);
}
/* relocation hunks */
reloc_hunk(f,ls,gv,R_ADDR32,HUNK_ABSRELOC32);
if (ls->flags & SF_EHFPPC)
reloc_hunk(f,ls,gv,R_REL26,HUNK_RELRELOC26);
reloc_hunk(f,ls,gv,R_REL14,HUNK_RELRELOC16);
reloc_hunk(f,ls,gv,R_REL14_BRTAKEN,HUNK_RELRELOC16);
reloc_hunk(f,ls,gv,R_REL14_BRNTAKEN,HUNK_RELRELOC16);
reloc_hunk(f,ls,gv,R_REL32,HUNK_RELRELOC32);
reloc_hunk(f,ls,gv,R_BASEREL16,HUNK_DREL16);
unsupp_relocs(ls); /* print unsupported relocations */
/* external references and global definitions */
ext_refs(f,ls);
ext_defs(f,ls,SYMB_GLOBAL,SYM_RELOC,EXT_DEF);
ext_defs(f,ls,SYMB_GLOBAL,SYM_ABS,EXT_ABS);
if (exthunk)
fwrite32be(f,0); /* close HUNK_EXT block, if required */
if (!gv->strip_symbols) {
/* symbol table */
ext_defs(f,ls,SYMB_LOCAL,SYM_RELOC,EXT_SYMB);
ext_defs(f,ls,SYMB_LOCAL,SYM_ABS,EXT_IGNORE);
if (symhunk)
fwrite32be(f,0); /* close HUNK_SYMBOL block, if required */
unsupp_symbols(ls); /* print unsupported symbol definitions */
}
fwrite32be(f,HUNK_END); /* end of this section */
ls = nextls;
}
}
static void writeexec(struct GlobalVars *gv,FILE *f)
/* creates a target-amigaos executable file (which is relocatable too) */
{
struct LinkedSection *ls = (struct LinkedSection *)gv->lnksec.first;
struct LinkedSection *nextls;
int i=0;
fwrite32be(f,HUNK_HEADER);
fwrite32be(f,0); /* resident libraries no longer supp. since OS2.0 */
if (ls->n.next == NULL) {
/* special case: no sections, create dummy section */
fwrite32be(f,1);
fwrite32be(f,0);
fwrite32be(f,0);
fwrite32be(f,0);
fwrite32be(f,HUNK_CODE);
fwrite32be(f,0);
fwrite32be(f,HUNK_END);
return;
}
fwrite32be(f,gv->nsecs); /* number of sections - no overlay support! @@@ */
fwrite32be(f,0);
fwrite32be(f,gv->nsecs-1);
/* write section size specifiers */
while (nextls = (struct LinkedSection *)ls->n.next) {
fwrite32be(f,((ls->flags&(SF_FAST|SF_CHIP))<<24) | ((ls->size+3)>>2));
ls = nextls;
i++;
}
if (i != gv->nsecs)
ierror("writeexec(): %d sections in list, but it should be %d",
i,gv->nsecs);
/* section loop */
ls = (struct LinkedSection *)gv->lnksec.first;
while (nextls = (struct LinkedSection *)ls->n.next) {
exthunk = symhunk = FALSE;
switch (ls->type) { /* section type */
case ST_CODE:
fwrite32be(f,HUNK_CODE);
break;
case ST_DATA:
fwrite32be(f,HUNK_DATA);
break;
case ST_UDATA:
fwrite32be(f,HUNK_BSS);
break;
default:
ierror("writeexec(): Illegal section type %d",ls->type);
break;
}
if (ls->flags & SF_UNINITIALIZED) {
fwrite32be(f,(ls->size+3)>>2); /* bss - size only */
}
else {
fwrite32be(f,(ls->filesize+3)>>2); /* initialized section size */
fwritex(f,ls->data,ls->filesize); /* write section contents */
fwrite_align(f,2,ls->filesize);
}
/* relocation hunks */
reloc_hunk(f,ls,gv,R_ADDR32,HUNK_ABSRELOC32);
unsupp_relocs(ls); /* print unsupported relocations */
if (!gv->strip_symbols) {
/* symbol table */
ext_defs(f,ls,SYMB_GLOBAL,SYM_RELOC,EXT_SYMB);
ext_defs(f,ls,SYMB_GLOBAL,SYM_ABS,EXT_IGNORE);
ext_defs(f,ls,SYMB_LOCAL,SYM_RELOC,EXT_SYMB);
ext_defs(f,ls,SYMB_LOCAL,SYM_ABS,EXT_IGNORE);
if (symhunk)
fwrite32be(f,0); /* close HUNK_SYMBOL block, if required */
unsupp_symbols(ls); /* print unsupported symbol definitions */
}
fwrite32be(f,HUNK_END); /* end of this section */
ls = nextls;
}
}
static void hunk_name_len(FILE *f,char *name)
/* writes a string in hunk-format style, i.e. first longword contains */
/* strlen in longwords and then follows the string itself, long-aligned */
{
size_t l=strlen(name);
fwrite32be(f,l?((l+3)>>2):0);
fwritex(f,name,l);
fwrite_align(f,2,l);
}
static void hunk_name(FILE *f,char *name)
/* writes a longword-aligned string, like hunk_name_len(), but */
/* without writing the length */
{
size_t l=strlen(name);
fwritex(f,name,l);
fwrite_align(f,2,l);
}
static int strlen32(char *s)
/* strlen in 32-bit words */
{
int l=strlen(s);
return (l?((l+3)>>2):0);
}
static void reloc_hunk(FILE *f,struct LinkedSection *sec,
struct GlobalVars *gv,uint8 r,uint32 relhunk)
/* generate an EHF relocation hunk for a specific reloc type */
{
struct Reloc *nextrel,*rel=(struct Reloc *)sec->relocs.first;
struct list **rlist=alloc(gv->nsecs*sizeof(struct list *));
int *rcnt=alloczero(gv->nsecs*sizeof(int)); /* reloc cnt for all sect. */
bool hunk_required=FALSE;
int i;
for (i=0; i<gv->nsecs; i++) { /* empty reloc lists for each section */
rlist[i] = alloc(sizeof(struct list));
initlist(rlist[i]);
}
while (nextrel = (struct Reloc *)rel->n.next) {
if (rel->type == r) {
/* move reloc node of correct type into relocssect's rlist */
remnode(&rel->n);
addtail(rlist[rel->relocsect.lnk->index],&rel->n);
rcnt[rel->relocsect.lnk->index]++;
hunk_required = TRUE;
}
rel = nextrel;
}
if (hunk_required) { /* there's at least one relocation */
fwrite32be(f,relhunk); /* reloc hunk id */
for (i=0; i<gv->nsecs; i++) {
if (rcnt[i]) {
fwrite32be(f,(uint32)rcnt[i]); /* number of relocations */
fwrite32be(f,(uint32)i); /* section index */
/* store relocation offsets */
while(rel = (struct Reloc *)remhead(rlist[i])) {
fwrite32be(f,(uint32)rel->offset);
#ifndef FASTALLOC
free(rel);
#endif
}
}
}
fwrite32be(f,0); /* no more relocation entries */
}
#ifndef FASTALLOC
/* free dynamically allocated rlists and rcnt array */
for (i=0; i<gv->nsecs; free(rlist[i++]));
free(rcnt);
#endif
}
static void unsupp_relocs(struct LinkedSection *sec)
{
struct Reloc *rel;
while (rel = (struct Reloc *)remhead(&sec->relocs)) {
error(32,fff_amigaos.tname,reloc_name[rel->type],sec->name,
rel->offset);
#ifndef FASTALLOC
free(rel);
#endif
}
}
static void ext_refs(FILE *f,struct LinkedSection *sec)
{
struct list xnodelist; /* xrefs with same ref. type and symbol name */
struct XRefNode *xn,*nextxn;
struct XReference *xref;
initlist(&xnodelist);
while (xref = (struct XReference *)remhead(&sec->xrefs)) {
char *name = xref->name; /* name of xref'ed symbol */
uint8 rtype = xref->type;
/* reference type mapping */
switch (rtype) {
case R_ADDR32:
rtype = EXT_ABSREF32;
break;
case R_REL14:
case R_REL14_BRTAKEN:
case R_REL14_BRNTAKEN:
case R_ADDR16:
rtype = EXT_RELREF16;
break;
case R_BASEREL16:
rtype = EXT_DEXT16; /* small data */
break;
case R_REL26:
rtype = EXT_RELREF26;
break;
default:
error(32,fff_amigaos.tname,reloc_name[xref->type],sec->name,
xref->offset);
rtype = EXT_RELREF8; /* @@@ to keep the loop running */
break;
}
/* search appropriate XRefNode for referenced symbol and type */
xn = (struct XRefNode *)xnodelist.first;
while (nextxn = (struct XRefNode *)xn->n.next) {
if (!strcmp(name,xn->sym_name) && rtype==xn->ref_type)
break;
xn = nextxn;
}
if (nextxn==NULL) { /* we have to create a new XRefNode? */
xn = alloc(sizeof(struct XRefNode));
xn->sym_name = name;
xn->ref_type = rtype;
xn->noffsets = 0;
initlist(&xn->xreflist);
addtail(&xnodelist,&xn->n);
}
/* add new offset to xreflist for same ref. type and symbol name */
addtail(&xn->xreflist,&xref->n);
xn->noffsets++;
}
if (xnodelist.first->next) { /* at least one reference in this section? */
if (!exthunk) {
exthunk = TRUE;
fwrite32be(f,HUNK_EXT);
}
while (xn = (struct XRefNode *)remhead(&xnodelist)) {
fwrite32be(f,((uint32)xn->ref_type << 24) | strlen32(xn->sym_name));
hunk_name(f,xn->sym_name); /* symbol's name */
fwrite32be(f,(uint32)xn->noffsets); /* number of references */
while (xref = (struct XReference *)remhead(&xn->xreflist)) {
fwrite32be(f,(uint32)xref->offset); /* offset */
#ifndef FASTALLOC
free(xref);
#endif
}
#ifndef FASTALLOC
free(xn);
#endif
}
}
}
static void ext_defs(FILE *f,struct LinkedSection *sec,uint8 bind,
uint8 stype,uint32 xdeftype)
{
struct Symbol *nextsym,*sym=(struct Symbol *)sec->symbols.first;
bool xdefs = FALSE;
int i;
while (nextsym = (struct Symbol *)sym->n.next) {
if (sym->type==stype && sym->bind==bind && sym->info<=SYMI_FUNC) {
remnode(&sym->n);
if (xdeftype != EXT_IGNORE) {
if (!xdefs) {
xdefs = TRUE;
if (xdeftype == EXT_SYMB) {
if (!symhunk) {
symhunk = TRUE;
fwrite32be(f,HUNK_SYMBOL);
}
}
else {
if (!exthunk) {
exthunk = TRUE;
fwrite32be(f,HUNK_EXT);
}
}
}
/* generate xdef or symbol table entry */
fwrite32be(f,(xdeftype << 24) | strlen32(sym->name));
hunk_name(f,sym->name); /* symbol's name */
fwrite32be(f,sym->value); /* ... and its value */
#ifndef FASTALLOC
free(sym);
#endif
}
}
sym = nextsym;
}
}
static void unsupp_symbols(struct LinkedSection *sec)
{
struct Symbol *sym;
while (sym = (struct Symbol *)remhead(&sec->symbols)) {
error(33,fff_amigaos.tname,sym->name,sym_bind[sym->bind],
sym_type[sym->type],sym_info[sym->info]);
#ifndef FASTALLOC
free(sym);
#endif
}
}
#endif